package edu.nus.mtech.sem1.ca.data;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import edu.nus.mtech.sem1.ca.Payment;
import edu.nus.mtech.sem1.ca.auth.User;
import edu.nus.mtech.sem1.ca.auth.UserRole;
import edu.nus.mtech.sem1.ca.cust.Customer;
import edu.nus.mtech.sem1.ca.cust.Status;
import edu.nus.mtech.sem1.ca.cust.complaint.Complaint;
import edu.nus.mtech.sem1.ca.cust.complaint.ComplaintStatus;
import edu.nus.mtech.sem1.ca.exception.ConfigurationException;
import edu.nus.mtech.sem1.ca.exception.DataInputFormatException;
import edu.nus.mtech.sem1.ca.exception.DataloadException;
import edu.nus.mtech.sem1.ca.exception.DuplicateException;
import edu.nus.mtech.sem1.ca.plan.Account;
import edu.nus.mtech.sem1.ca.plan.Plan;
import edu.nus.mtech.sem1.ca.plan.PlanType;
import edu.nus.mtech.sem1.ca.plan.valueadd.ValueAddedService;
import edu.nus.mtech.sem1.ca.plan.valueadd.ValueAddedType;
import edu.nus.mtech.sem1.ca.service.Service;
import edu.nus.mtech.sem1.ca.txn.CallType;
import edu.nus.mtech.sem1.ca.txn.Transaction;
import edu.nus.mtech.sem1.ca.utils.IOUtils;
import edu.nus.mtech.sem1.ca.utils.Logger;
import edu.nus.mtech.sem1.ca.utils.TelephonyConstants;
import edu.nus.mtech.sem1.ca.utils.TelephonyUtils;


/**
 * Loads Configuration and pre-saved data into the Database class. 
 *
 */
public class DatabaseLoader {

	private static DatabaseLoader singletonInstance;
	
	private static Properties applicationConfiguration=null;
	
	private Database database;
	
	public static DatabaseLoader getInstance(){
		if (singletonInstance==null){
			singletonInstance=new DatabaseLoader();
			
		}
		return singletonInstance;
	}
	
	public static DatabaseLoader getInstance(Properties applicationConfiguration){
		if (singletonInstance==null){
			singletonInstance=new DatabaseLoader(applicationConfiguration);
			
		}
		return singletonInstance;
	}
	
	/**
	 * Called via singleton constructor. Loads data from files into database
	 * @param applicationConfiguration - Custom configuration
	 */
	private DatabaseLoader(Properties applicationConfiguration){

		this.applicationConfiguration=applicationConfiguration;
		initialize();
			
		
	}
	
	/**
	 * Called via singleton constructor. Loads data from files into database
	 */
	private DatabaseLoader(){
		//1.loads application configuration from properties file
		loadConfiguration(); 
		initialize();
	}
	
	private void initialize() {
		try {

			//1. Create an empty database
			createNewBlankDatabase(); 
			
			//2.load all CSV files into Database including user, userrole, customer, complaints, txn info
			loadDataFromFileSystem(database); 
			
		} catch (ConfigurationException e) {
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG);
			e.printStackTrace();
			System.exit(1);
		} catch (DataloadException e) {
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_DATA);
			e.printStackTrace();
			System.exit(1);
		}
		
	}

	private void createNewBlankDatabase() {
		database=Database.getInstance();
	}

	private void loadDataFromFileSystem(Database dbase) throws ConfigurationException, DataloadException{
		loadUserData(dbase); //Load credential and role information from users.csv
		loadUserRoleAccess(dbase); //Load Access for various users from UserRoles.properties
		loadCustomerData(dbase); //load customer data from customers.csv
		loadCustomerComplaintData(dbase); //load complaint data from complaints.csv
		
		loadRateData(dbase);//load rate data from rates.csv
		loadTransactionData(dbase); //load txn data from transactions.csv
		loadAccountPlanMappingData(dbase);//load account plan mapping data from account_plan_mapping_details.csv
		loadPaymentHistoryMappingData(dbase);//load payment history plan mapping data from paymentHistory.csv
	}

	private void loadUserData(Database dbase) {

		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> userDataStringList=null;
        try {        	
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.USER_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachUserInformation = null;
            userDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachUserInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	userDataStringList.add(eachUserInformation);
            }
            
            //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (userDataStringList.size()>0){ 
            	storeUserInformationInDatabase(userDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}


	/**
	 * Parses each line of userinformation and converts into objects
	 * @param userDataStringList
	 * @throws DataInputFormatException 
	 */
	private void storeUserInformationInDatabase(List<String> userDataStringList) throws DataInputFormatException {

		Map<String, User> userMap=new HashMap<String, User>();
		String[] splitUserInfo=null;
		User user=null;
		for (String eachUserLine: userDataStringList){
			user=new User();
			
			splitUserInfo=eachUserLine.split(TelephonyConstants.COMMA);
			if (splitUserInfo.length>2){
				user.setUserName(splitUserInfo[0]);
				user.setPassword(splitUserInfo[1]);
				user.setUserRole(UserRole.valueOf(splitUserInfo[2]));
				
				userMap.put(user.getUserName(), user);
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("User Information :"+TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		
		database.setUserMap(userMap);
		
	}


	private void loadUserRoleAccess(Database dbase) throws ConfigurationException, DataloadException {

		FileInputStream fileInputStream=null;
		
		try {
			Properties properties=new Properties();
			fileInputStream=new FileInputStream(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.USER_ROLE_PROP_FILE_LOCATION_KEY));
			properties.load(fileInputStream);
			dbase.setUserRoleAccessControlMapping(getUserRoleMappingFromProperties(properties));
		} catch (FileNotFoundException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
			throw new ConfigurationException(TelephonyConstants.ERR_COULD_NOT_LOAD_USER_ROLE_FILE+e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(e.getMessage());
			throw new DataloadException(TelephonyConstants.ERR_COULD_NOT_LOAD_USER_ROLE_DATA+e.getMessage());
		}
		finally{
			IOUtils.closeQuietly(fileInputStream);
		}
	}


	/**
	 * Transforms Key<String>, Value<String> into Key<String> Value<ArrayList> 
	 * 
	 * @param properties
	 * @return
	 */
	private Map<String, List<String>> getUserRoleMappingFromProperties(Properties properties) {
		if (properties==null || properties.size()==0){
			return null;
		}
		Map<String, List<String>> userRoleAccessControlMapping=new HashMap<String, List<String>>();
		String key=null;
		String value=null;
		String[] tempSplit=null;
		for (Entry entry:properties.entrySet()){
			key=(String)entry.getKey();
			value=(String)entry.getValue();
			//reinitializing for next loop
			tempSplit=null;
			
			if (value!=null && value.contains(TelephonyConstants.COMMA)){
				tempSplit=value.split(TelephonyConstants.COMMA);
			}
			else{
				tempSplit=new String[]{value};
			}
			userRoleAccessControlMapping.put(key, Arrays.asList(tempSplit));
			
		}
			
		return userRoleAccessControlMapping;
	}




	public Database getDatabase() {
		return database;
	}

	public void setDatabase(Database database) {
		this.database = database;
	}

	
	/**
	 * Loads configuration from the file into a Property 
	 */
	private void loadConfiguration() {

		InputStream inputStream=null;
		
		try {
			applicationConfiguration=new Properties();
			//TODO fix this. Use resourcestream instead of fileinputstream
			//inputStream=TelephonyApplicationConfiguration.class.getResourceAsStream(TelephonyConstants.TELEPHONY_APPLICATION_PROPERTIES_FILE_NAME);
			inputStream=new FileInputStream(TelephonyConstants.TELEPHONY_APPLICATION_PROPERTIES_FILE_NAME);
			applicationConfiguration.load(inputStream);
			
			Logger.log("Application Configuration is......"+applicationConfiguration);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}
		finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(inputStream);
		}
	}
	
	/**
	 * Returns value for a input key from the configuration file - in our case TelephonyApplication.properties
	 * @param key
	 * @return
	 */
	public static String getValueOfConfigurationKey(String key){
		//Loading from a property file. So, it is safe to assume that the value is always a String
		//Ah yes. unless the key is null
		if (key ==null){
			return ((String)applicationConfiguration.get(key));	
		}
		else{
			System.err.println(TelephonyConstants.ERR_KEY_NOT_FOUND_FOR_PROPERTY+"::"+key);
			
		}
		return "";
	}
	
	
	/**
	 * Loads customer data into database
	 * @param dbase
	 */
	private void loadCustomerData(Database dbase) {

		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> customerDataStringList=null;
        try {
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.CUSTOMER_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachCustomerInformation = null;
            customerDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachCustomerInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	customerDataStringList.add(eachCustomerInformation);
            }
            
          //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (customerDataStringList.size()>0){
            	storeCustomerInformationInDatabase(customerDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}


	/**
	 * Parses each line of Customerinformation and converts into objects
	 * @param customerDataStringList
	 * @throws DataInputFormatException 
	 */
	private void storeCustomerInformationInDatabase(List<String> customerDataStringList) throws DataInputFormatException {

		Map<String, Customer> customerMap=new HashMap<String, Customer>();
		String[] splitUserInfo=null;
		Customer customer=null;
		for (String eachCustomerLine: customerDataStringList){
			customer=new Customer();
			
			splitUserInfo=eachCustomerLine.split(TelephonyConstants.SEMICOLON);
			if (splitUserInfo.length>6){
				//NRIC;NAME;ADDRESS;INTERESTS;EMAIL;STATUS;ACCOUNT_NUMBER
				customer.setNric(splitUserInfo[0]);
				customer.setName(splitUserInfo[1]);
				customer.setAddress(splitUserInfo[2]);
				customer.setInterests(splitUserInfo[3]);
				customer.setEmail(splitUserInfo[4]);
				customer.setStatus(Status.valueOf(splitUserInfo[5]));
				customer.setAccountNo(splitUserInfo[6]);
				
				customerMap.put(customer.getNric(), customer);
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("Customer Information : "+TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		
		database.setCustomerMap(customerMap);
		
	}
	
	
	/**
	 * Loads customer data into database
	 * @param dbase
	 */
	private void loadCustomerComplaintData(Database dbase) {

		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> complaintDataStringList=null;
        try {
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.COMPLAINT_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachComplaintInformation = null;
            complaintDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachComplaintInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	complaintDataStringList.add(eachComplaintInformation);
            }
            
          //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (complaintDataStringList.size()>0){
            	storeComplaintInformationInDatabase(complaintDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}


	/**
	 * Parses each line of Customerinformation and converts into objects
	 * @param complaintDataStringList
	 * @throws DataInputFormatException 
	 */
	private void storeComplaintInformationInDatabase(List<String> complaintDataStringList) throws DataInputFormatException {

		Map<String, Set<Complaint>> complaintMap=new HashMap<String, Set<Complaint>>();
		String[] splitUserInfo=null;
		Complaint complaint=null;
		
		for (String eachComplaintLine: complaintDataStringList){
			complaint=new Complaint();
			
			splitUserInfo=eachComplaintLine.split(TelephonyConstants.SEMICOLON);
			if (splitUserInfo.length>6){
				//NRIC;ACCOUNT_NO;SERVICE;COMPLAINT_NO;COMPLAINT_DATE;COMPLAINT_DESC;STATUS
				complaint.setNric(splitUserInfo[0]);
				complaint.setAccountNo(splitUserInfo[1]);
				complaint.setService(Service.valueOf(splitUserInfo[2]));
				complaint.setComplaintNo(splitUserInfo[3]);
				
				complaint.setComplaintDate(TelephonyUtils.getDateFromString(splitUserInfo[4])); //format parsing happens in the setter
				
				complaint.setComplaintDesc(splitUserInfo[5]);
				complaint.setStatus(ComplaintStatus.valueOf(splitUserInfo[6]));
				
				populateIntoComplaintMap(complaint.getNric(), complaint, complaintMap);
				
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("Complaint Information :"+TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		
		database.setComplaintMap(complaintMap);
	}


	private void populateIntoComplaintMap(String nric, Complaint complaint, Map<String, Set<Complaint>> complaintMap) {

		if (complaintMap.containsKey(nric)){
			Set<Complaint> complaintSet=complaintMap.get(nric);
			complaintSet.add(complaint); //automatically eliminates duplicates
			complaintMap.put(nric, complaintSet);
		}
		else{
			Set<Complaint> complaintSet=new HashSet<Complaint>();
			complaintSet.add(complaint);
			complaintMap.put(complaint.getNric(), complaintSet);	
		}
	}
	
	
	/**
	 * Loads customer data into database
	 * @param dbase
	 */
	private void loadRateData(Database dbase) {

		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> rateDataStringList=null;
        try {
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.RATE_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachRateInformation = null;
            rateDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachRateInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	rateDataStringList.add(eachRateInformation);
            }
            
          //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (rateDataStringList.size()>0){
            	storeRateInformationInDatabase(rateDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}


	/**
	 * Parses each line of Customerinformation and converts into objects
	 * @param rateDataStringList
	 * @throws DataInputFormatException 
	 */
	private void storeRateInformationInDatabase(List<String> rateDataStringList) throws DataInputFormatException {

		Map<String, Float> rateMap=new HashMap<String, Float>();
		String[] splitRateInfo=null;
		//Variables created for clarity. 
		String tempDescription;
		Float tempRate=null;
		for (String eachRateLine: rateDataStringList){
			splitRateInfo=eachRateLine.split(TelephonyConstants.COMMA);
			if (splitRateInfo.length>1){
				tempDescription=splitRateInfo[0];
				tempRate=TelephonyUtils.getSafeFloatFromString(splitRateInfo[1]);
				
				rateMap.put(tempDescription, tempRate);
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("Rate Information: "+ TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		
		database.setRateMap(rateMap);
		
	}


	/**
	 * Loads Transaction data into database
	 * @param dbase
	 */
	private void loadTransactionData(Database dbase) {

		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> txnDataStringList=null;
        try {
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.TXN_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachTxnInformation = null;
            txnDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachTxnInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	txnDataStringList.add(eachTxnInformation);
            }
            
          //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (txnDataStringList.size()>0){
            	storeTransactionInformationInDatabase(txnDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}



	private void storeTransactionInformationInDatabase(List<String> transactionDataStringList) throws DataInputFormatException {

		Map<String, Set<Transaction>> transactionMap=new HashMap<String, Set<Transaction>>();
		String[] splitUserInfo=null;
		Transaction transaction=null;
		
		for (String eachTransactionLine: transactionDataStringList){
			transaction=new Transaction();
			
			splitUserInfo=eachTransactionLine.split(TelephonyConstants.COMMA);
			/*Logger.log("eachTransactionLine...."+eachTransactionLine);
			Logger.log("split user info"+Arrays.asList(splitUserInfo));*/
			//Logger.log(splitUserInfo.length);
			if (splitUserInfo.length>6){
				//CUST_LINE_NUMBER	NUMBER_CALLED	DATE_OF_CALL	TIME_OF_CALL	DURATION_MIN	LINE_TYPE	CALL_TYPE
				transaction.setLineNumber(splitUserInfo[0]);
				transaction.setNumberCalled(splitUserInfo[1]);
				transaction.setDateOfCall(TelephonyUtils.getDateFromString(splitUserInfo[2]));
				transaction.setTimeOfCall(TelephonyUtils.getSafeIntegerFromString(splitUserInfo[3]));
				transaction.setDurationOfCall(TelephonyUtils.getSafeFloatFromString(splitUserInfo[4])); //format parsing happens in the setter
				transaction.setLineType(PlanType.valueOf(splitUserInfo[5]));
				transaction.setCallType(CallType.valueOf(splitUserInfo[6]));
				
				populateIntoTransactionMap(transaction.getLineNumber(), transaction, transactionMap);
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("Transaction Information:" +TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		
		database.setTransactionMap(transactionMap);
	}

	

	private void populateIntoTransactionMap(String lineNumber, Transaction transaction, Map<String, Set<Transaction>> transactionMap) {

		if (transactionMap.containsKey(lineNumber)){
			Set<Transaction> transactionSet=transactionMap.get(lineNumber);
			transactionSet.add(transaction); //automatically eliminates duplicates
			transactionMap.put(lineNumber, transactionSet);
		}
		else{
			Set<Transaction> transactionSet=new HashSet<Transaction>();
			transactionSet.add(transaction);
			transactionMap.put(transaction.getLineNumber(), transactionSet);	
		}
	}
	
	private void loadAccountPlanMappingData(Database dbase) {

		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> accountPlanMappingDataStringList=null;
        try {
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.ACCOUNT_PLAN_MAPPING_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachAccountPlanMappingInformation = null;
            accountPlanMappingDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachAccountPlanMappingInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	accountPlanMappingDataStringList.add(eachAccountPlanMappingInformation);
            }
            
          //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (accountPlanMappingDataStringList.size()>0){
            	storeAccountPlanMappingInformationInDatabase(accountPlanMappingDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}


	/**
	 * Parses each line of userinformation and converts into objects
	 * @param accountPlanMappingStringList
	 * @throws DataInputFormatException 
	 * @throws DuplicateException 
	 */
	private void storeAccountPlanMappingInformationInDatabase(List<String> accountPlanMappingStringList) throws DataInputFormatException, DuplicateException {

		/*
ACCOUNT_NUMBER	PLAN_TYPE	VALUE_ADDED_TYPE	DATE_COMMENCED	DATE_TERMINATED	TELEPHONE_NUMBER	SUBSCRIPTION_STATUS
A00001	VOICE		10/1/2011	NA	90050969	ACTIVE
A00001	MOBILE		10/1/2011	NA	90050969	ACTIVE

		 */
		Map<String, Account> accountMap=new HashMap<String, Account>();
		String[] splitAccountPlanInfo=null;
		Account account=null;
		Plan plan=null;
		String tempAccountNumber=null;
		String tempPlanId=null;
		int count=0;
		for (String eachAccountLine: accountPlanMappingStringList){
			count++;
			
			splitAccountPlanInfo=eachAccountLine.split(TelephonyConstants.COMMA);
			//Logger.log(eachAccountLine);
			/*for (String s: splitAccountPlanInfo){
				Logger.log(s);
			}*/
			
			if (splitAccountPlanInfo.length>8){
				if (count==1){
					//this is the first line. So create a new account
					account=new Account();
					plan=new Plan();
					tempAccountNumber=splitAccountPlanInfo[0];
					tempPlanId=splitAccountPlanInfo[1];
					account.setAccountNumber(tempAccountNumber);
					plan.setPlanId(tempPlanId);
				}
				else if (!(tempAccountNumber.equalsIgnoreCase(splitAccountPlanInfo[0]))){
					//Next account has begun. create a new account but before that add the earlier map into a map
					account.setAccountNumber(tempAccountNumber);
					accountMap.put(account.getAccountNumber(), account);
					
					account=new Account();
					plan=new Plan();
					tempAccountNumber=splitAccountPlanInfo[0];
					tempPlanId=splitAccountPlanInfo[1];
					account.setAccountNumber(tempAccountNumber);
					plan.setPlanId(tempPlanId);
				}
				else if (!(tempPlanId.equalsIgnoreCase(splitAccountPlanInfo[1]))){
					plan=new Plan();
					tempPlanId=splitAccountPlanInfo[1];
					plan.setPlanId(tempPlanId);
				}
				
				if (splitAccountPlanInfo[3]!=null && !("".equalsIgnoreCase(splitAccountPlanInfo[3].trim()))){ //wont be null. Just empty spaces. Ideally, we should be checking for the accepted values
					ValueAddedService valueAddedService=new ValueAddedService();
					valueAddedService.setValueAddedId(splitAccountPlanInfo[3]);
					valueAddedService.setValueAddedType(ValueAddedType.valueOf(splitAccountPlanInfo[4]));
					
					//It's a value Added Service Entry. So, just add the valued added Service to the plan. All other details are value added service details
					
					valueAddedService.setDateCommenced(TelephonyUtils.getDateFromString(splitAccountPlanInfo[5]));
					valueAddedService.setDateTerminated(splitAccountPlanInfo[6].toString().equals("NA")?null: TelephonyUtils.getSafeDate(splitAccountPlanInfo[6]));
					valueAddedService.setTelephoneNumber(splitAccountPlanInfo[7]);
					valueAddedService.setAdditionalChannels(splitAccountPlanInfo[8]);
					valueAddedService.setSubscriptionStatus(Status.valueOf(splitAccountPlanInfo[9]));
					
					account.addValueAddedServiceToPlan(plan, valueAddedService);
					//skip to the next row
					continue;
				}
				else{
					//It's a new Plan. Hurray !!!
					//DATE_COMMENCED	DATE_TERMINATED	TELEPHONE_NUMBER	SUBSCRIPTION_STATUS
					plan.setPlanType(PlanType.valueOf(splitAccountPlanInfo[2]));
					plan.setDateCommenced(TelephonyUtils.getDateFromString(splitAccountPlanInfo[5]));
					
					plan.setDateTerminated(splitAccountPlanInfo[6].toString().equals("NA")?null:TelephonyUtils.getSafeDate(splitAccountPlanInfo[6]));
					plan.setTelephoneNumber(splitAccountPlanInfo[7]);
					plan.setSubscriptionStatus(Status.valueOf(splitAccountPlanInfo[9]));

					account.addPlan(plan);
				}
				
				
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("User Information :"+TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		//zaw modified to add last account to the account map.
		if(account!=null){
			accountMap.put(account.getAccountNumber(), account);
		}
		
		database.setAccountMap(accountMap);
		
	}
	
	private void loadPaymentHistoryMappingData(Database dbase){
		BufferedReader bufferedReader = null;
		FileReader fileReader=null;
		List<String> paymentHistoryDataStringList=null;
        try {
        	fileReader=new FileReader(System.getProperty("user.dir")+"\\"+ applicationConfiguration.getProperty(TelephonyConstants.PAYMENT_HISTORY_DATA_FILE_LOCATION_KEY));
            bufferedReader = new BufferedReader(fileReader);
            String eachPaymentInformation = null;
            paymentHistoryDataStringList=new ArrayList<String>();
            int lineNumber=0;
            while ((eachPaymentInformation = bufferedReader.readLine()) != null) {
            	lineNumber++;
            	if (lineNumber==1){
            		continue; //skip header 
            	}
            	paymentHistoryDataStringList.add(eachPaymentInformation);
            }
            
          //Zaw commented. If there's no data in data file, map should be created atleast to avoid null map.
            //if (paymentHistoryDataStringList.size()>0){
            	storePaymentHistoryMappingData(paymentHistoryDataStringList);
            //}
            
        } catch (FileNotFoundException e) {
        	e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}catch (Exception e) {
			e.printStackTrace();
			System.err.println(TelephonyConstants.ERR_COULD_NOT_LOAD_APP_CONFIG+ e.getMessage());
			System.exit(1);
		}finally{
			//To avoid the too many files open. Not that it matters in a small application like this but still... you never know
			IOUtils.closeQuietly(bufferedReader);
			IOUtils.closeQuietly(fileReader);
		}
	}
	
	private void storePaymentHistoryMappingData(List<String> paymentHistoryDataStringList) throws DataInputFormatException{
		Map<String, Payment> paymentHistoryMap=new HashMap<String, Payment>();
		String[] splitPaymentHistoryInfo=null;
		Payment payment=null;
		for (String eachPaymentHistoryLine: paymentHistoryDataStringList){
			payment=new Payment();
			
			splitPaymentHistoryInfo=eachPaymentHistoryLine.split(TelephonyConstants.COMMA);
			if (splitPaymentHistoryInfo.length>2){
				payment.setAccountNos(splitPaymentHistoryInfo[0]);
				payment.setpaidAmt(Float.valueOf(splitPaymentHistoryInfo[1]));
				payment.setpreviousBal(Float.valueOf(splitPaymentHistoryInfo[2]));
			
				paymentHistoryMap.put(payment.getAccountNos(), payment);
				
			}
			else{
				System.err.println(TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
				throw new DataInputFormatException("Payment History :"+TelephonyConstants.ERR_WRONG_DATA_INPUT_FORMAT);
			}
		}
		
		database.setPaymentHistoryMap(paymentHistoryMap);
		
	}

	public static Properties getApplicationConfiguration() {
		return applicationConfiguration;
	}

	public static void setApplicationConfiguration(
			Properties applicationConfiguration) {
		DatabaseLoader.applicationConfiguration = applicationConfiguration;
	}
	
}
